package jp.co.systemi.asp.workflow.group;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import jp.co.systemi.asp.utils.BeanUtils;
import jp.co.systemi.asp.utils.DModalWindow;
import jp.co.systemi.asp.utils.SelectOption;
import jp.co.systemi.asp.workflow.ICallback;
import jp.co.systemi.asp.workflow.Main;
import jp.co.systemi.asp.workflow.annotation.FormInput;
import jp.co.systemi.asp.workflow.annotation.LengthBetween;
import jp.co.systemi.asp.workflow.annotation.Required;
import jp.co.systemi.asp.workflow.data.Group;
import jp.co.systemi.asp.workflow.data.GroupMeta;
import jp.co.systemi.asp.workflow.data.User;
import jp.co.systemi.asp.workflow.data.UserGroup;
import jp.co.systemi.asp.workflow.data.UserGroupMeta;
import jp.co.systemi.asp.workflow.data.UserMeta;
import jp.co.systemi.asp.workflow.peer.UserPeer;
import jp.co.systemi.asp.workflow.user.UserSelect;

import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.authorization.strategies.role.Roles;
import org.apache.wicket.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.markup.html.form.DateTextField;
import org.apache.wicket.extensions.yui.calendar.DatePicker;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.PropertyModel;
import org.datanucleus.util.StringUtils;
import org.slim3.datastore.Datastore;

import com.google.appengine.api.datastore.Transaction;

/**
 * グループ登録 Mainから継承すること
 *
 * @author wzhang
 *
 */
@AuthorizeInstantiation(Roles.ADMIN)
public class GroupReg extends Main implements ICallback {
    /**
     * ログ出力 クラス名をあわせること
     */
    private static final Logger log = Logger
            .getLogger(GroupReg.class.getName());
    public static final String UNIQUE_CODE = "uniqeGroupCode";
    public static final String UNIQUE_NAME = "uniqeGroupName";
    /**
     * 画面データを保持するモデル できるだけ、データオブジェクトで代用すること 代用できない場合は、本ファイルにstatic モデルクラスを定義すること
     * 画面間のモデルの共通化より、モジュール分割、カプセルを重視する
     */
    private Group model = new Group();
    // 編集用オブジェクト
    private UserGroup edit = new UserGroup();

    /*
     * 以下入力項目を定義する
     */
    // 基本情報
    /**
     * 画面入力項目を定義する Formに項目をadd、項目にValidatorをaddの記述を省略するため、 publicで定義すること
     * SuperクラスのbindValidators関数はすべてに入力項目を拾って、
     * Formにaddする、各項目にValidatorをaddする（実装手続き）
     * これにより、各画面のコーディングは実装手続きを簡略化し、業務ロジックに専念できる
     */
    /**
     * 入力項目のValidatorをAnnotationで定義する 画面項目定義書からそのまま転記するイメージ
     */
    @FormInput
    @Required
    @LengthBetween(minlength = 1, maxlength = 32)
    public TextField<String> groupCode = new TextField<String>("wi_groupCode",
            new PropertyModel<String>(model, "code"));
    @FormInput
    @Required
    @LengthBetween(minlength = 1, maxlength = 32)
    public TextField<String> groupName = new TextField<String>("wi_groupName",
            new PropertyModel<String>(model, "name"));
    @FormInput
    public TextField<Integer> groupSeq = new TextField<Integer>("wi_groupSeq",
            new PropertyModel<Integer>(model, "seq"));
    @FormInput
    public TextArea<String> groupNotes = new TextArea<String>("wi_groupNotes",
            new PropertyModel<String>(model, "notes"));
    @FormInput
    public DropDownChoice<SelectOption> choiceParentGroup = null;
    @FormInput
    public DateTextField startDate = new DateTextField("wi_startDate",
            new PropertyModel<Date>(model, "startDate"),
            getString("date.format.yyyyMM"));
    // 新規登録時には、解散日付を入力しない
    // モードの応じて、ボタン名を変更する
    @FormInput
    public AjaxButton submit = new AjaxButton("wi_submit", new PropertyModel<String>(
            this, "buttonName")) {
        private static final long serialVersionUID = 4677635908244480648L;
        @Override
        protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            // 入力項目の必須、桁数などのチェックはValidatorで実施済み

            // ここから、相関チェック、業務ロジックだけを記述する

            // グループコードが重複しないこと

            // グループ名が重複しないこと

            // 同じ上位組織の中にシーケンスがなくでもいい、重複してもいい、
            // ソートする時に、SEQ、code順で行う
            Transaction tx = Datastore.beginTransaction();
            if (mode == MODE.NEW) {
                // ユニックコード制御
                if (!Datastore.putUniqueValue(UNIQUE_CODE, model.getCode())) {
                    error(getString("group.code.exists"));
                    target.addComponent(feedback);
                    return;
                }
                // ユニックグループ名制御
                if (!Datastore.putUniqueValue(UNIQUE_NAME, model.getName())) {
                    error(getString("group.name.exists"));
                    target.addComponent(feedback);
                    return;
                }
            }
            if (parentGroup != null) {
                GroupMeta gm = GroupMeta.get();
                Group pGroup = Datastore.query(gm).filter(gm.code.equal(parentGroup.getKey())).asSingle();
                if(pGroup != null){
                    model.setParentGroup(pGroup.getCode());
                }
                else{
                    model.setParentGroup(null);
                }
            }
            Datastore.put(tx, model);
            tx.commit();
            PageParameters pars = new PageParameters();
            pars.add("code", model.getCode());
            setResponsePage(GroupRef.class, pars);
        }
    };
    /**
     * 新規、編集モードに応じて、ボタンの名前を変更する
     */
    @SuppressWarnings("unused")
    private String buttonName = "";

    // ポジションはLisｔViewを利用する
    /**
     * データストアーから初期化する入力項目であれば、コンストラクター中で生成するしかない
     */
    final List<UserGroup> userGroups = new ArrayList<UserGroup>();
    private SelectOption parentGroup = new SelectOption("", "");

    public GroupReg(PageParameters pars) {
        String code = pars.getString("code");
        GroupMeta gm = GroupMeta.get();
        Group group = Datastore.query(gm).filter(gm.code.equal(code)).asSingle();
        if(group == null){
            error(getString("data.error"));
            group = new Group();
        }
        BeanUtils.copy(model, group);
        groupCode.setEnabled(false);
        mode = MODE.EDIT;
        buttonName = "保存";
        if (!StringUtils.isEmpty(model.getParentGroup())) {
            Group parentGroupOrg = Datastore.query(gm).filter(gm.code.equal(model.getParentGroup())).asSingle();
            if (parentGroupOrg == null) {
                model.setParentGroup(null);
            } else {
                parentGroup.setKey(parentGroupOrg.getCode());
                parentGroup.setValue(parentGroupOrg.getName());
            }
        }
        init();
    }

    /**
     * コンストラクター 画面の生成、イベント処理の定義はすべて、この中で完結する
     */
    public GroupReg() {
        mode = MODE.NEW;
        init();
    }

    private void init() {
        // データストアーから初期化する入力項目の生成は一番先に行う
        // グループ一覧を取得
        GroupMeta gm = GroupMeta.get();
        List<Group> groupsOrg = Datastore.query(gm).asList();
        // グループ一覧からグループの選択リスト入力項目を生成する
        List<SelectOption> groups = new ArrayList<SelectOption>();
        if (!StringUtils.isEmpty(parentGroup.getKey())) {
            groups.add(new SelectOption("", getString("empty.option")));
        }
        for (Group group : groupsOrg) {
            // 自分自身を除く
            if (group.getCode().equals(model.getCode())) {
                continue;
            }
            // 自分の下位組織も除く
            boolean isBelow = false;
            Group chkParent = group;
            while (chkParent != null
                    && !StringUtils.isEmpty(chkParent.getParentGroup())) {
                if (chkParent.getParentGroup().equals(model.getCode())) {
                    isBelow = true;
                    break;
                }
                chkParent = Datastore.query(gm).filter(gm.code.equal(chkParent.getParentGroup())).asSingle();
            }
            if (isBelow) {
                continue;
            }
            groups.add(new SelectOption(group.getCode(), group.getName()));
        }
        ChoiceRenderer<SelectOption> choiceRenderer = new ChoiceRenderer<SelectOption>(
                "value", "key");
        choiceParentGroup = new DropDownChoice<SelectOption>("wi_parentGroup",
                new PropertyModel<SelectOption>(this, "parentGroup"), groups,
                choiceRenderer);
        // Formを生成する
        final Form<Group> form = new MainForm<Group>(this, "wi_groupRegForm"); // 複数サブミットボタンがありまして、動きが異なる場合は、各ボタンのonSubmitにて実装

        // ポジションリスト
        final WebMarkupContainer tblPosition = new WebMarkupContainer(
                "wi_tblPosition");
        tblPosition.setOutputMarkupId(true);
        form.add(tblPosition);
        // positions.addAll(model.getPositions());
        userGroups.clear();
        UserGroupMeta ugm = UserGroupMeta.get();
        userGroups.addAll(Datastore.query(ugm).filter(ugm.groupCode.equal(model.getCode())).sort(ugm.roleSeq.asc).asList());
        tblPosition.add(new ListView<UserGroup>("wi_trPosition", userGroups) {
            private static final long serialVersionUID = 4846361379394092695L;

            @Override
            protected void populateItem(ListItem<UserGroup> item) {
                final UserGroup userGroup = item.getModelObject();
                UserMeta um = UserMeta.get();
                User user = Datastore.query(um).filter(um.loginId.equal(userGroup.getLoginId())).asSingle();
                if(user != null){
                    userGroup.setUserName(UserPeer.getName(user));
                }
                item.add(new Label("wi_tdSeq", new PropertyModel<Integer>(
                        userGroup, "roleSeq")));
                item.add(new Label("wi_tdRole", new PropertyModel<String>(
                        userGroup, "role")));

                item.add(new Label("wi_tdOwnerName", new PropertyModel<String>(
                        userGroup, "userName")));
                item.add(new AjaxLink<Object>("wi_tdDelete") {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        log.info("delete");
                        Datastore.delete(userGroup.getId());
                        userGroups.remove(userGroup);
                        // 順番を振りなおし
                        int i = 0;
                        for (UserGroup ug : userGroups) {
                            ug.setSeq(++i);
                        }
                        target.addComponent(form);
                    }

                });
                item.add(new AjaxLink<Object>("wi_tdEdit") {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        log.info("edit");
                        Datastore.delete(userGroup.getId());
                        userGroups.remove(userGroup);
                        edit.setRole(userGroup.getRole());
                        edit.setUserName(userGroup.getUserName());
                        edit.setLoginId(userGroup.getLoginId());
                        // 順番を振りなおし
                        int i = 0;
                        for (UserGroup ug : userGroups) {
                            ug.setSeq(++i);
                        }
                        target.addComponent(form);
                    }

                });
            }
        });
        tblPosition.add(new AjaxButton("wi_addPosition") {
            private static final long serialVersionUID = -2573012794536636414L;

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                // 入力チェック
                if (StringUtils.isEmpty(edit.getRole())) {
                    error(getString("role.empty"));
                    target.addComponent(feedback);
                    return;
                }
                if (StringUtils.isEmpty(edit.getLoginId())) {
                    error(getString("owner.empty"));
                    target.addComponent(feedback);
                    return;
                }
                // 重複チェック
                for (UserGroup userGroup : userGroups) {
                    if (edit.getLoginId().equals(userGroup.getLoginId())) {
                        error(getString("role.duplicate"));

                        target.addComponent(feedback);
                        return;
                    }
                }
                // オーナーの存在チェック
                UserMeta um = UserMeta.get();

                User user = Datastore.query(um).filter(um.loginId.equal(edit.getLoginId())).asSingle();
                if (user == null) {
                    error(getString("user.notexists"));

                    target.addComponent(feedback);
                    return;
                }
                // 登録する
                UserGroup userGroup = new UserGroup();
                userGroup.setRole(edit.getRole());
                userGroup.setGroupCode(model.getCode());
                userGroup.setLoginId(edit.getLoginId());
                userGroup.setRoleSeq(userGroups.size() + 1);
                UserGroupMeta ugm = UserGroupMeta.get();
                List<UserGroup> userGroupsOfTheUser = Datastore.query(ugm).filter(ugm.loginId.equal(userGroup.getLoginId())).asList();
                userGroup.setSeq(userGroupsOfTheUser.size() + 1);
                if (userGroup.getSeq() == 1) {
                    userGroup.setMain(true);
                }
                Datastore.put(userGroup);

                edit.setRole("");
                edit.setLoginId("");
                List<User> users = Datastore.query(um).filter(um.loginId.equal(userGroup.getLoginId())).asList();
                if (users.size() > 0) {
                    user = users.get(0);
                    userGroup.setUserName(UserPeer.getName(user));
                }
                userGroups.add(userGroup);
                target.addComponent(feedback);
                target.addComponent(tblPosition);
            }
        });
        tblPosition.add(new TextField<String>("wi_role",
                new PropertyModel<String>(edit, "role")));
        tblPosition.add(new TextField<String>("wi_ownerName",
                new PropertyModel<String>(edit, "userName")));
        // 役割のオーナーを選択あるモデルウィンドウ
        final DModalWindow ownerSelectWindow = new DModalWindow(
                "wi_selectOwnerModelWindow");
        ownerSelectWindow.setTitle("オーナー選択");
        ownerSelectWindow.setPageCreator(new ModalWindow.PageCreator() {
            private static final long serialVersionUID = -6364600063465337078L;

            public Page createPage() {
                return new UserSelect(GroupReg.this, ownerSelectWindow);
            }
        });

        ownerSelectWindow
                .setOKActionCallback(new ModalWindow.WindowClosedCallback() {
                    private static final long serialVersionUID = -1012735996565367949L;

                    public void onClose(AjaxRequestTarget target) {
                        target.addComponent(feedback);
                        target.addComponent(tblPosition);
                    }
                });
        ownerSelectWindow
                .setCancelActionCallback(new ModalWindow.WindowClosedCallback() {
                    private static final long serialVersionUID = -1012735996565367949L;

                    public void onClose(AjaxRequestTarget target) {
                        target.addComponent(feedback);
                        target.addComponent(tblPosition);
                    }
                });
        // 閉じるボタン（X）をクリックした時に呼ばれる
        ownerSelectWindow
                .setCloseButtonCallback(new ModalWindow.CloseButtonCallback() {
                    private static final long serialVersionUID = -2205343604513004971L;

                    public boolean onCloseButtonClicked(AjaxRequestTarget target) {
                        return true;
                    }
                });
        main.add(ownerSelectWindow);

        tblPosition.add(new AjaxButton("wi_selectOwner") {
            private static final long serialVersionUID = 6474399847088717250L;

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                ownerSelectWindow.setWindowClosedCallback(null);
                ownerSelectWindow.show(target);
                log.info("after show dialog");
            }
        });
        if (mode == MODE.NEW) {
            tblPosition.setVisible(false);
        }

        startDate.add(new DatePicker());
    }

    /**
     * 選択ダイアログのOKボタンを押下された時にのCALLBACK PageParametersにloginId=>選択したEmail
     */
    public void setResult(PageParameters pars) {
        log.info("setResult is called.");
        edit.setLoginId(pars.getString("loginId"));
        edit.setUserName(pars.getString("userName"));
    }

    /**
     * ユーザー選択ダイアログインターフェースのメソッド 現在の選択値をダイアログの初期表示時に表示させるため
     */
    public Object getDefault(PageParameters pars) {
        return edit.getLoginId();
    }

    public String getEmptyMessage(PageParameters pars) {
        return getString("owner.empty");
    }
}
